﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AddIn.EA.Framework.Common;

namespace AddIn.EA.Framework
{
    /// <summary>
    /// Class that represent a list of elements.
    /// </summary>
    public class ElementList : BaseList<Element>
    {
        #region Constructors
        /// <summary>
        /// Get an instance of an element list.
        /// </summary>
        /// <param name="reference">The reference of EA Object Model element collection</param>
        /// <param name="parent"></param>
        public ElementList(EA_Application.Collection reference, IElement parent)
            : base(reference, parent) { }
        #endregion

        #region Public Methods
        /// <summary>
        /// Add new element in the element list
        /// </summary>
        /// <param name="name">The name of element</param>
        /// <param name="type">The type of element</param>
        /// <returns>An element instance</returns>
        public Element AddNew(string name, ElementType type)
        {
            return base.AddNew(name, type.ToString());
        }
        /// <summary>
        /// Add new element in the element list
        /// </summary>
        /// <param name="name">The name of element</param>
        /// <returns>An element instance</returns>
        public override Element AddNew(string name)
        {
            return base.AddNew(name, "class");
        }

        /// <summary>
        /// This method don´t shall be used.
        /// </summary>
        public override Element AddNew(string name, string type)
        {
            throw new InvalidOperationException("This method cannot be used in this context. Try to use the method AddNew(string, ElementType)!");
        }
        #endregion
    }

    /// <summary>
    /// Class that represent an element.
    /// </summary>
    public class Element : BaseElement<EA_Application.Element>
    {
        #region Fields
        private AttributeList _attributes;
        private ElementList _elements;
        private MethodList _methods;
        private TaggedValueList _taggedValues;
        private ConnectorList _connectors;
        private ScenarioList _scenarios;
        #endregion

        #region Constructors
        /// <summary>
        /// Get an instance of Element
        /// </summary>
        /// <param name="reference">The reference of EA Object Model element</param>
        /// <param name="parent">The parent element</param>
        public Element(EA_Application.Element reference, IElement parent)
            : base(reference, parent)
        {
            base.Reference.Gentype = "C#";
            base.Reference.Update();
        }
        #endregion

        #region Properties

        #region BaseElement Members
        /// <summary>
        /// The element id
        /// </summary>
        public override int ID
        {
            get { return this.Reference.ElementID; }
        }
        /// <summary>
        /// The element name
        /// </summary>
        public override string Name
        {
            get
            {
                return this.Reference.Name;
            }
            set
            {
                this.Reference.Name = value;
            }
        }
        #endregion

        /// <summary>
        /// The element alias. Used to decorate an object with C# standard attribute.
        /// </summary>
        public string Alias
        {
            get { return this.Reference.Alias; }
            set { this.Reference.Alias = value; }
        }
        /// <summary>
        /// The element Abstract. Used to define an object as abstract.
        /// </summary>
        public string Abstract
        {
            get { return this.Reference.Abstract; }
            set { this.Reference.Abstract = value; }
        }
        /// <summary>
        /// The attribute collection for element.
        /// </summary>
        public AttributeList Attributes
        {
            get
            {
                if (_attributes == null)
                    _attributes = new AttributeList(this.Reference.Attributes, this);

                return _attributes;
            }
        }
        /// <summary>
        /// The element collection for element.
        /// </summary>
        public ElementList Elements
        {
            get
            {
                if (_elements == null)
                    _elements = new ElementList(this.Reference.Elements, this);

                return _elements;
            }
        }
        /// <summary>
        /// Notes for element.
        /// </summary>
        public string Notes
        {
            get { return this.Reference.Notes; }
            set { this.Reference.Notes = value; }
        }
        /// <summary>
        /// The element scope. Can be Public, Private, Protected or Internal.
        /// </summary>
        public ElementScope Scope
        {
            get
            {
                if (String.IsNullOrEmpty(this.Reference.Visibility))
                    return default(ElementScope);

                try
                {
                    return (ElementScope)Enum.Parse(typeof(ElementScope), this.Reference.Visibility, true);
                }
                catch { return default(ElementScope); }
            }

            set { this.Reference.Visibility = value.ToString(); }
        }
        /// <summary>
        /// The element status.
        /// </summary>
        public string Status
        {
            get { return this.Reference.Status; }
            set { this.Reference.Status = value; }
        }
        /// <summary>
        /// The element stereotype.
        /// </summary>
        public string Stereotype
        {
            get { return this.Reference.Stereotype; }
            set { this.Reference.Stereotype = value; }
        }
        /// <summary>
        /// The element type.
        /// </summary>
        public ElementType Type
        {
            get
            {
                if (String.IsNullOrEmpty(this.Reference.Type))
                    return default(ElementType);

                try
                {
                    return (ElementType)Enum.Parse(typeof(ElementType), this.Reference.Type, true);
                }
                catch { return default(ElementType); }
            }

            set { this.Reference.Type = value.ToString(); }
        }
        /// <summary>
        /// The methods collection for element.
        /// </summary>
        public MethodList Methods
        {
            get
            {
                if (_methods == null)
                    _methods = new MethodList(this.Reference.Methods, this);

                return _methods;
            }
        }
        /// <summary>
        /// The tagged values collection for element.
        /// </summary>
        public TaggedValueList TaggedValues
        {
            get
            {
                if (_taggedValues == null)
                    _taggedValues = new TaggedValueList(this.Reference.TaggedValues, this);

                return _taggedValues;
            }
        }
        /// <summary>
        /// A list of connectors associated with this element which represents the other elements which this element is connected with.
        /// </summary>
        public ConnectorList Connectors
        {
            get
            {
                if (_connectors == null)
                    _connectors = new ConnectorList(this.Reference.Connectors, this);
                return _connectors;
            }
        }
        /// <summary>
        /// A list of connectors associated with this element which represents the other elements which this element is connected with.
        /// </summary>
        public ScenarioList Scenarios
        {
            get
            {
                if (_scenarios == null)
                    _scenarios = new ScenarioList(this.Reference.Scenarios, this);
                return _scenarios;
            }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Add parent class for element. Used for class type element.
        /// </summary>
        /// <param name="baseClass">The parent class name</param>
        public void AddBaseClass(string baseClass)
        {
            if (this.Type == ElementType.Class)
            {
                string genLink = string.Empty;

                if (this.Reference.Genlinks != null)
                {
                    genLink = this.Reference.Genlinks;

                    while (genLink.IndexOf("Parent") >= 0)
                    {
                        string parentString = this.Reference.Genlinks.Substring(this.Reference.Genlinks.IndexOf("Parent"));
                        parentString = parentString.Substring(0, parentString.IndexOf(";"));
                        genLink = genLink.Replace(parentString, "");
                    }
                }

                genLink += string.Format("Parent={0};", baseClass);

                this.Reference.Genlinks = genLink;
                this.Reference.Update();
            }
        }
        /// <summary>
        /// Add parent implementations for element. Used for class or interface type element.
        /// </summary>
        /// <param name="interfaces">List of interface names</param>
        public void AddImplementedInterfaces(params string[] interfaces)
        {
            if (this.Type == ElementType.Class || this.Type == ElementType.Interface)
            {
                string implements = this.Reference.Genlinks;

                foreach (string interfaceImplementation in interfaces)
                    implements = string.Format("Implements={0};{1}", interfaceImplementation, implements);

                if (implements.Length > 0)
                {
                    this.Reference.Genlinks = implements;
                    this.Reference.Update();
                }
            }
        }
        /// <summary>
        /// Get an attribute from element by attribute name.
        /// </summary>
        /// <param name="name">The attribute name.</param>
        /// <returns>The object attribute of element.</returns>
        public AttributeClass GetAttributeByName(string name)
        {
            return this.Attributes.GetReferenceByName(name);
        }

        /// <summary>
        /// Get a method from element by method name.
        /// </summary>
        /// <param name="name">The method name.</param>
        /// <returns>The object method of element.</returns>
        public Method GetMethodByName(string name)
        {
            return this.Methods.GetReferenceByName(name);
        }
        /// <summary>
        /// Get a tag value from element by tag name.
        /// </summary>
        /// <param name="name">The tag value name.</param>
        /// <returns>The object tag value of element.</returns>
        public TaggedValue GetTaggedValueByName(string name)
        {
            return this.TaggedValues.GetReferenceByName(name);
        }
        /// <summary>
        /// Refresh the child elements of this on Project Browser of EA.
        /// </summary>
        public void Refresh()
        {
            this.Reference.Refresh();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string GetNamespace()
        {
            return GetParentNamespace(this.Parent);
        }
        /// <summary>
        /// Create a connection between this element and another one.
        /// </summary>
        /// <param name="targetElement">The target element to be connected with.</param>
        /// <param name="type">The type of connection</param>
        /// <param name="direction">The direction of the connection</param>
        public Connector AddConnector(Element targetElement, Connector.ConnectorType type, Connector.ConnectorDirection direction)
        {
            Connector connector = null;

            if (this.Type == ElementType.Class &&
                targetElement.Type == ElementType.Class)
            {
                connector = this.Connectors.AddNew(this.Name + "_" + targetElement.Name, type);
                connector.ClientID = targetElement.ID;
                connector.Direction = direction;
            }
            else
                throw new InvalidOperationException("The both elements must be type \"Class\" to create the connection");

            return connector;
        }
        #endregion

        #region Private Methods
        private string GetParentNamespace(ISimpleElement parent)
        {
            if (parent != null)
            {
                if (parent is Package && !((Package)parent).IsNamespaceRoot)
                {
                    string parentNamespace = GetParentNamespace(parent.Parent);
                    if (!String.IsNullOrEmpty(parentNamespace))
                        return string.Format("{0}.{1}", parentNamespace, ((IElement)parent).Name);
                    else
                        return ((IElement)parent).Name;
                }
                else if (parent is Package && ((Package)parent).IsNamespaceRoot)
                    return ((IElement)parent).Name;
                else
                    return GetParentNamespace(parent.Parent);
            }
            else
            {
                return string.Empty;
            }
        }
        #endregion

    }
}
